namespace Maple.Branch.AuthServer
{
    using Base.Consts;
    using Configurations;
    using Consts;
    using Localizations;
    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.DataProtection;
    using Microsoft.AspNetCore.Identity;
    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.DependencyInjection;
    using StackExchange.Redis;
    using Volo.Abp;
    using Volo.Abp.AspNetCore.Mvc.Localization;
    using Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic;
    using Volo.Abp.Auditing;
    using Volo.Abp.Autofac;
    using Volo.Abp.EventBus.RabbitMq;
    using Volo.Abp.Modularity;

    [DependsOn(
        typeof(AbpAutofacModule),
        typeof(AbpEventBusRabbitMqModule),
        typeof(AbpAspNetCoreMvcUiBasicThemeModule),
        typeof(AuthServerEfCoreModule)
    )]
    public class AuthServerWebHostModule : AbpModule
    {
        public override void PreConfigureServices(ServiceConfigurationContext context)
        {
            context.Services.PreConfigure<AbpMvcDataAnnotationsLocalizationOptions>(options =>
            {
                options.AddAssemblyResource(typeof(AuthServerLocalResource), typeof(AuthServerWebHostModule).Assembly);
            });

            PreConfigure<IMvcBuilder>(mvcBuilder =>
            {
                mvcBuilder.AddApplicationPartIfNotExists(typeof(AuthServerWebHostModule).Assembly);
            });
        }

        public override void ConfigureServices(ServiceConfigurationContext context)
        {
            var configuration = context.Services.GetConfiguration();

            Configure<AbpAuditingOptions>(options =>
            {
                options.IsEnabledForGetRequests = true;
                options.ApplicationName = AuthServerConsts.ModuleName;
            });

            ConfigureRedis(context);

            ConfigureAuthentication(context);

            RegisterAuthServerConfiguration(context);
        }

        public override void OnApplicationInitialization(ApplicationInitializationContext context)
        {
            var app = context.GetApplicationBuilder();

            app.UseCorrelationId();

            app.UseVirtualFiles();

            app.UseRouting();

            app.UseAbpRequestLocalization();

            if (BranchConsts.IsMultiTenancyEnabled)
            {
                app.UseMultiTenancy();
            }

            app.UseIdentityServer();
            app.UseAuthentication();
            app.UseAuthorization();

            app.UseAuditing();

            app.UseConfiguredEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });
        }

        private void ConfigureRedis(ServiceConfigurationContext context)
        {
            var configuration = context.Services.GetConfiguration();

            context.Services.AddStackExchangeRedisCache(options =>
            {
                options.Configuration = configuration["Redis:Configuration"];
            });

            // TODO: ConnectionMultiplexer.Connect call has problem since redis may not be ready when this service has started!
            var redis = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]);

            context.Services.AddDataProtection()
                .PersistKeysToStackExchangeRedis(redis, "Branch-DataProtection-Keys");
        }

        private void ConfigureAuthentication(ServiceConfigurationContext context)
        {
            context.Services
                   .GetObject<IdentityBuilder>()
                   .AddDefaultTokenProviders()
                   .AddSignInManager();

            context.Services.AddAuthentication(o =>
            {
                o.DefaultScheme = IdentityConstants.ApplicationScheme;
                o.DefaultSignInScheme = IdentityConstants.ExternalScheme;
            }).AddIdentityCookies();
        }

        private void RegisterAuthServerConfiguration(ServiceConfigurationContext context)
        {
            var configuration = context.Services.GetConfiguration();

            var authServerConfig = new AuthServerConfiguration();

            var authConfig = configuration.GetSection(AuthServerConfiguration.AuthServerPath);

            authConfig.GetSection(AuthServerConfiguration.ApiResourcePath).Bind(authServerConfig.ApiResources);

            authConfig.GetSection(AuthServerConfiguration.ClientPath).Bind(authServerConfig.Clients);

            context.Services.AddSingleton(authServerConfig);
        }
    }
}
